using System;
using System.Collections.Generic;

using Atomic.Libraries.Mathematics;

namespace Atomic.Libraries.Plotting.Functions
{
	[Serializable]
	public class LinearInterpolationFunction : PlotFunction, IRangedPlotFunction
	{
		public LinearInterpolationFunction(params IPlotPoint[] points)
			: this((IEnumerable<IPlotPoint>)points)
		{
		}

		public LinearInterpolationFunction(IEnumerable<IPlotPoint> points)
		{
			Points = new ReadOnlyList<IPlotPoint>(points, (p1, p2) => p1.X.CompareTo(p2.X));

			if (Points.Count == 0)
			{
				XMin = double.NaN;
				XMax = double.NaN;
			}
			else
			{
				XMin = Points[0].X;
				XMax = Points[Points.Count - 1].X;
			}

			/*// Also useful to store derivate function (i.e. a piecewise constant step function).
			List<StepFunctionStep> steps = new List<StepFunctionStep>();
			for (int i = 1; i < Points.Count; i++)
			{
				steps.Add(new StepFunctionStep(Points[i - 1].X, Points[i].X, (Points[i].Y - Points[i - 1].Y) / (Points[i].X - Points[i - 1].X)));
			}
			Derivative = new StepFunction(steps, 0.0, 0.0);*/
		}

		public override double Value(double x)
		{
			if (Points.Count == 0)
			{
				return double.NaN;
			}

			if (x <= Points[0].X)
			{
				return Points[0].Y;
			}

			for (int i = 1; i < Points.Count; i++)
			{
				if (x <= Points[i].X)
				{
					double lambda = (Points[i].X - x) / (Points[i].X - Points[i - 1].X);
					return (1.0 - lambda) * Points[i].Y + lambda * Points[i - 1].Y;
				}
			}

			return Points[Points.Count - 1].Y;
		}

		public LinearInterpolationFunction Add(double x, double y)
		{
			return Add(new PlaneVector(x, y));
		}

		public LinearInterpolationFunction Add(IPlotPoint point)
		{
			List<IPlotPoint> points = new List<IPlotPoint>(Points);
			points.Add(point);
			return new LinearInterpolationFunction(points);
		}

		public ReadOnlyList<IPlotPoint> Points
		{
			get;
			private set;
		}

		/*public StepFunction Derivative
		{
			get;
			private set;
		}*/

		public double XMin
		{
			get;
			private set;
		}

		public double XMax
		{
			get;
			private set;
		}
	}
}
